#ifndef PHOTON_MAPPING_H
#define PHOTON_MAPPING_H

#include "src/scene/energyColor.h"
#include "src/scene/photon.h"
#include "src/scene/render.h"
#include "src/scene/nearestInfo.h"
#include "src/scene/photonMap.h"

#include <vector>

#define WITHOUT_FILTER 0
#define CONE_FILTER 1
#define GAUSSIAN_FILTER 2

#define USE_K_VALUE 0
#define USE_COLOR_VALUE 1
#define USE_BOTH_VALUES 2

class PhotonMapping : public Render {
    public:
        PhotonMapping(Scene *, unsigned int _maxPhotons = 5000, unsigned int _causticMaxPhotons = 1000,
                      unsigned int _maxNearestPhotons = 100, unsigned int _causticMaxNearestPhotons = 10,
                      double _maxRadius = 2, double _causticMaxRadius = 0.1,
                      unsigned int _photonDepth = 4, unsigned int _raytracingDepth = 4,
                      unsigned short _filter = 0, double _coneFilterConstant = 5, bool _keepFirstBounce = false);
        ~PhotonMapping();

        void setPhotonDepth(unsigned int _d) {photonDepth = _d;};
        const unsigned int &getPhotonDepth() const {return photonDepth;};

        void setMaxPhotons(unsigned int _max) {maxPhotons = _max;};
        const unsigned int &getMaxPhotons() const {return maxPhotons;};

        void setCausticMaxPhotons(unsigned int _max) {causticMaxPhotons = _max;};
        const unsigned int &getCausticMaxPhotons() const {return causticMaxPhotons;};

        void setMaxNearestPhotons(unsigned int _max) {maxNearestPhotons = _max;};
        const unsigned int &getMaxNearestPhotons() const {return maxNearestPhotons;};

        void setCausticMaxNearestPhotons(unsigned int _max) {causticMaxNearestPhotons = _max;};
        const unsigned int &getCausticMaxNearestPhotons() const {return causticMaxNearestPhotons;};

        void setMaxRadius(double _max) {maxRadius = _max;};
        const double &getMaxRadius() const {return maxRadius;};

        void setCausticMaxRadius(double _max) {causticMaxRadius = _max;};
        const double &getCausticMaxRadius() const {return causticMaxRadius;};

        void setFilter(unsigned short _f) {filter = _f;};
        const unsigned short &getFilter() const {return filter;};

        void setConeFilterConstant(double _c) {coneFilterConstant = _c;};
        const double &getConeFilterConstant() const {return coneFilterConstant;};

        void setTestReflexion(unsigned short _t) {testReflexion = _t;};
        const unsigned short &getTestReflexion() const {return testReflexion;};

        void setKeepFirstBounce(bool _b) {keepFirstBounce = _b;};
        const bool &getKeepFirstBounce() const {return keepFirstBounce;};


        unsigned int getMapsSize() const {return maps.size();};
        unsigned int getMapISize(unsigned int _i) const {return maps[_i]->getKdTree()->getPhotonsSize();};

        //photon mapping de fato
        bool emitPhotons(bool);
        void drawPhotons() const;

        void clearMaps();

        void print() const;
    private:
        //emissão
        unsigned int maxPhotons;
        unsigned int causticMaxPhotons;

        //radiancia
        unsigned int maxNearestPhotons;
        unsigned int causticMaxNearestPhotons;
        double maxRadius;
        double causticMaxRadius;

        unsigned int photonDepth;

        unsigned short filter;
        double coneFilterConstant;

        bool keepFirstBounce;

        unsigned short testReflexion;

        std::vector<PhotonMap *> maps;

        void testHit(const Ray &, const EnergyColor &, unsigned int, unsigned int, short);
        short int russianRoulette(const NearestInfo &, const EnergyColor &, double *, double *, double *);
        short int russianRoulette(const NearestInfo &) const;
        bool absorbPhoton(const NearestInfo &, const EnergyColor &);
        void propagatePhoton(const NearestInfo &, const Ray &, const EnergyColor &, unsigned int, /*Photon *, */unsigned int, short);

//        Object *causticsIn(Object *);
//        void addToMap(Photon *, Object *);

        //raytracing
        Color calculate(const Ray &, unsigned int, bool = false) const;
        Color findColor(const NearestInfo &, const Vector3D &, unsigned int, Object *, bool) const;
        EnergyColor radianceEstimate(const Vector3D &, const Vector3D &) const;
        std::vector<NearestPhoton> locateNearestPhotons(const Vector3D &) const;

        double calculateWeight(double, double) const;


};

#endif
